/**
 * 
 * Copyright (C) 2011 Cody Stoutenburg . All rights reserved.
 *
 *       This program is free software; you can redistribute it and/or
 *       modify it under the terms of the GNU Lesser General Public License
 *       as published by the Free Software Foundation; either version 2.1
 *       of the License, or (at your option) any later version.
 *
 *       This program is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *       GNU Lesser General Public License for more details.
 *
 *       You should have received a copy of the GNU Lesser General Public License
 *       along with this program; if not, write to the Free Software
 *       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 * 
 */
package server;

import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import registry.RmiRegistry;
import server.shared.ChatInterface;
import server.shared.SynchronisationServer;
import client.shared.Message;
import client.shared.MessageReceiver;

/**
 * @author Cody Stoutenburg
 * 
 */
public class Server extends UnicastRemoteObject
		implements
			ChatInterface,
			SynchronisationServer {
	private static final long serialVersionUID = 1496088088713167363L;
	private static String NAME_SEPARATOR = "#";

	private final String addressRmiRegistry;
	private final String serverName;
	private final SynchronizedList<MessageReceiver> serverList;

	public Server(String address, String serverName) throws RemoteException {
		addressRmiRegistry = address;

		serverList = new SynchronizedList<MessageReceiver>();
		this.serverName = serverName;

		String[] allNames;
		try {
			allNames = RmiRegistry.list(addressRmiRegistry);
			for (String name : allNames) {
				Remote remoteObject = RmiRegistry.lookup(name);
				if (remoteObject instanceof SynchronisationServer) {
					// we find a chat server
					SynchronisationServer chatServer = (SynchronisationServer) remoteObject;
					this.serverList.addCollection(chatServer.getAllReceiver());
					break;
				}
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}

	private void clientConnected(MessageReceiver receiver) {
		String[] allNames;
		try {
			allNames = RmiRegistry.list(addressRmiRegistry);
			for (String name : allNames) {
				Remote remoteObject = RmiRegistry.lookup(name);

				if (remoteObject instanceof SynchronisationServer) {
					try {
						// we find a chat server
						SynchronisationServer chatServer = (SynchronisationServer) remoteObject;

						if (!chatServer.getServerName().equals(
								this.getServerName())) {
							chatServer.receiverConnected(receiver);
						}
					} catch (RemoteException e) {
						RmiRegistry.unbind(name); // the server is down
					}
				}
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}

	private void clientDisconnected(MessageReceiver receiver) {
		String[] allNames;
		try {
			allNames = RmiRegistry.list(addressRmiRegistry);
			for (String name : allNames) {
				Remote remoteObject = RmiRegistry.lookup(name);

				if (remoteObject instanceof SynchronisationServer) {
					// we find a chat server
					SynchronisationServer chatServer = (SynchronisationServer) remoteObject;
					try {
						if (!chatServer.getServerName().equals(
								this.getServerName())) {
							chatServer.receiverDisconnected(receiver);
						}
					} catch (RemoteException e) {
						RmiRegistry.unbind(name); // the server is down
					}
				}
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * ChatInterface implementation
	 */
	@Override
	public boolean testServer() throws RemoteException {
		return true;
	}

	@Override
	public String getServerName() {
		return serverName;
	}

	@Override
	public void join(MessageReceiver messageReceiver) {
		// add to our list
		serverList.add(messageReceiver);
		// notify all servers
		clientConnected(messageReceiver);

		serverList.incCounter();
		for (MessageReceiver client : serverList.getUnmodifiableList()) {
			try {
				client.joinMessage(messageReceiver.getName());
			} catch (RemoteException e) {
				// the client is not connected so remove from list
				disconnect(client);
			}
		}
		serverList.decCounter();
	}

	@Override
	public void sendMessage(String sender, String receivers, Message message) {
		serverList.incCounter();

		for (String receiver : getReceiversFromString(receivers)) {

			// find the client with the name
			for (MessageReceiver client : serverList.getUnmodifiableList()) {
				try {
					if (receiver.equals(client.getName())) {
						client.sendMessage(sender, message);
						break;
					}
				} catch (RemoteException e) {
					// the client is not connected so remove from list
					disconnect(client);
				}
			}
		}
		serverList.decCounter();
	}

	@Override
	public void disconnect(MessageReceiver messageReceiver) {
		serverList.remove(messageReceiver);
		clientDisconnected(messageReceiver);

		serverList.incCounter();
		for (MessageReceiver client : serverList.getUnmodifiableList()) {
			try {
				client.exitMessage(messageReceiver.getName());
			} catch (RemoteException e) {
				// the client is not connected so remove from list
				disconnect(client);
			}
		}
		serverList.decCounter();
	}

	@Override
	public Set<String> getAllConnectedPeople() throws RemoteException {
		Set<String> allNames = new HashSet<String>();
		serverList.incCounter();
		for (MessageReceiver client : serverList.getUnmodifiableList()) {
			allNames.add(client.getName());
		}
		serverList.decCounter();
		return allNames;
	}

	private Set<String> getReceiversFromString(String receivers) {
		Set<String> receiverList = new HashSet<String>();
		for (String receiver : receivers.split(NAME_SEPARATOR)) {
			receiverList.add(receiver);
		}
		return receiverList;
	}

	@Override
	public String formatReceivers(Set<String> allDestinataire)
			throws RemoteException {
		StringBuilder destinataire = new StringBuilder(100);

		for (String dest : allDestinataire) {
			destinataire.append(dest + NAME_SEPARATOR);
		}

		return destinataire.toString();
	}

	/**
	 * Synchronisation server implementation
	 */
	@Override
	public Collection<MessageReceiver> getAllReceiver() throws RemoteException {
		return this.serverList.getUnmodifiableList();
	}

	@Override
	public void receiverConnected(MessageReceiver receiver)
			throws RemoteException {
		this.serverList.add(receiver);
	}

	@Override
	public void receiverDisconnected(MessageReceiver receiver)
			throws RemoteException {
		serverList.remove(receiver);
	}
}
